﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Collections;
using System.Collections.Specialized;
using Microsoft.DirectX.DirectSound;

namespace MDXTest
{
    public partial class MainWindow : Form
    {
        private MicManager manager;
        private AutoResetEvent captureEvent;
        private MemoryStream MemStream;
        private string FileName;
        private Thread captureThread;

        public MainWindow()
        {
            InitializeComponent();
            this.manager = MicManager.getInstance();
            this.manager.Initialize();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            manager.DisposeCapture();
        }

        private void button_Record_Click(object sender, EventArgs e)
        {
            saveFileDialog1.AddExtension = true;
            saveFileDialog1.Filter = "AudioFile(*.wav)|*.wav";
            if (saveFileDialog1.ShowDialog() == DialogResult.Cancel)
            {
                return;
            }

            this.FileName = saveFileDialog1.FileName;
            this.MemStream = new MemoryStream();
            captureEvent = new AutoResetEvent(false);
            manager.StartCapture(10, captureEvent);
            captureThread = new Thread(new ThreadStart(CaptureThreadProc));
            captureThread.Start();
        }

        /// <summary>
        /// Recursive method that returns a target number in the form
        /// of an ArrayList of bytes with designated number of bytes. If not enough
        /// bytes to hold the targetnumber, will throw argumentexception.
        /// Should be used in a try-catch clause
        /// </summary>
        /// <param name="targetnumber">the value intended to convert</param>
        /// <param name="numofbytes">number of bytes needed</param>
        /// <returns></returns>
        private ArrayList ToBytes(int targetnumber, short numofbytes)
        {
            int remainder, result;
            ArrayList returningarray;

            ArgumentException wrongnumofbytes =
                new ArgumentException("Not enough bytes to represent number",
                "numofbytes");
            result = Math.DivRem(targetnumber, 256, out remainder);


            //if not enough bytes specified to represent target number
            if (targetnumber >= Math.Pow(256, (double)numofbytes))
            {
                throw wrongnumofbytes;
            }

            //if there are higher significant hexadecima, run a recursion
            if (result >= 1)
            {
                returningarray = ToBytes(result, (short)(numofbytes - 1));
                returningarray.Insert(0, Convert.ToByte(remainder));
                return returningarray;
            }

            else //if (result < 1) recursion terminating condition
            {
                returningarray = new ArrayList(numofbytes);
                returningarray.Add(Convert.ToByte(targetnumber));
                for (int i = 0; i < numofbytes - 1; i++)
                {
                    returningarray.Add(Convert.ToByte(0));//fill up most significant hexadecima with 0's
                }
                return returningarray;
            }
        }

        private void RecordData(string filepath)
        {
      
            ArrayList byteArrayList = new ArrayList();
            System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
            Stream MyStream = new FileStream(filepath, FileMode.Create);
            try
            {
                byteArrayList.AddRange(ascii.GetBytes("RIFF"));
                byteArrayList.AddRange(ToBytes(36 + (int)MemStream.Length, 4));
                byteArrayList.AddRange(ascii.GetBytes("WAVE"));

                //fmt  chunk
                byteArrayList.AddRange(ascii.GetBytes("fmt "));
                //length of fmt chunk (never changes)
                byteArrayList.AddRange(ToBytes(16, 4));
                //"1" for pcm encoding
                byteArrayList.AddRange(ToBytes(1, 2));
                byteArrayList.AddRange(ToBytes(manager.CaptureBuffer.Format.Channels, 2));
                byteArrayList.AddRange(ToBytes(manager.CaptureBuffer.Format.SamplesPerSecond, 4));
                byteArrayList.AddRange(ToBytes(manager.CaptureBuffer.Format.AverageBytesPerSecond, 4));
                byteArrayList.AddRange(ToBytes(manager.CaptureBuffer.Format.BlockAlign, 2));
                byteArrayList.AddRange(ToBytes(manager.CaptureBuffer.Format.BitsPerSample, 2));

                //the data chunk
                byteArrayList.AddRange(ascii.GetBytes("data"));
                byteArrayList.AddRange(ToBytes((int)MemStream.Length, 4));
                byte[] buffer = MemStream.ToArray();
                byteArrayList.AddRange(buffer);
                buffer = new byte[byteArrayList.Count];
                byteArrayList.CopyTo(buffer);
                MyStream.Write(buffer, 0, buffer.Length);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
            finally
            {
                MemStream.Close();
                MyStream.Close();
            }
        }

        private void CaptureThreadProc()
        {

            int bufferSize = manager.CaptureBuffer.Caps.BufferBytes;
            int capturePos, lastReadPos;
            manager.CaptureBuffer.GetCurrentPosition(out capturePos, out lastReadPos);

            do
            {
                captureEvent.WaitOne();
                int readPos;
                manager.CaptureBuffer.GetCurrentPosition(out capturePos, out readPos);
                int size = readPos - lastReadPos;
                if (size == 0)
                    continue;
                if (size < 0)
                    size += bufferSize;

                manager.CaptureBuffer.Read(lastReadPos, MemStream, size, LockFlag.None);
                System.Console.Write("lastReadPos :" + lastReadPos.ToString() + " ReadPos:" + readPos.ToString() + System.Environment.NewLine);
                //byte[] data = (byte[])(manager.CaptureBuffer.Read(lastReadPos, Type.GetType("System.Byte"), LockFlag.None, size));
                //data[data.Length - 1] = 0;
                //MemStream.Write(data, (int)MemStream.Seek(0, SeekOrigin.Current), data.Length - 1);

                lastReadPos = readPos;
            }
            while (manager.CaptureBuffer.Capturing);
        }

        private void button_Stop_Click(object sender, EventArgs e)
        {
            manager.StopCapture();
            RecordData(this.FileName);
            captureThread.Abort();
        }
    }
}
